/**
 * @file sim-spi_flash.c
 * @author LokLiang (lokliang@163.com)
 * @brief 模拟一个 SPI 和 FLASH 的硬件的部分功能，用于在纯软件环境中满足对 SPI FLASH 的需求
 * @version 0.1
 * @date 2023-04-12
 *
 * @copyright Copyright (c) 2023
 *
 */

#define CONFIG_SYS_LOG_DBG_ON 0
#include "sys_log.h"

#define _CHECK_EN(EXP)         \
    do                         \
    {                          \
        if (EXP)               \
        {                      \
            SYS_LOG_WRN(#EXP); \
            return false;      \
        }                      \
    } while (0)

static uint8_t s_nor[16][16][0x1000]; // 模拟数据存储单元(1MB)

static uint8_t const s_mfid = 0xEF; // WinBond

static struct
{
    struct // SFDP parameter header
    {
        char desc[4];
        uint8_t minor_rev;
        uint8_t major_rev;
        uint8_t npn;
        uint8_t reserve;
    } sfdp;

    struct // JEDEC basic parameter header
    {
        uint8_t id;        /**< Parameter ID LSB */
        uint8_t minor_rev; /**< Parameter minor revision */
        uint8_t major_rev; /**< Parameter major revision */
        uint8_t len;       /**< Parameter table length(in double words) */
        uint8_t ptp[3];    /**< Parameter table 24bit pointer (byte address) */
        uint8_t reserve;
    } basic;

    uint8_t table[4 * 9];

} const s_sfdp = {
    .sfdp = {
        .desc = {'S', 'F', 'D', 'P'},
        // note: 版本标记为：major_rev.minor_rev
        .minor_rev = 0,
        .major_rev = 1,
        .npn = 0,
    },

    .basic = {
        .id = 0,
        .minor_rev = 0,
        .major_rev = 1,
        .len = 9,
        .ptp = {16, 0, 0},
    },

    .table = {
        [0] = 1 | 1 << 2,
        [1] = 0x20,
        [4] = ((sizeof(s_nor) - 1) << 3) >> 0 & 0xFF,
        [5] = ((sizeof(s_nor) - 1) << 3) >> 8 & 0xFF,
        [6] = ((sizeof(s_nor) - 1) << 3) >> 16 & 0xFF,
        [7] = ((sizeof(s_nor) - 1) << 3) >> 24 & 0xFF,
        [28 + 0] = 12,
        [28 + 1] = 0x20,
    },
};

static struct
{
    union
    {
        u8_t value;
        struct
        {
            u8_t busy : 1; // 忙位
            u8_t wel : 1;  // 可写
            u8_t : 0;
        } bit;
    } reg; // 模拟内部寄存器

    bool select_flag; // 用于表示 CS 信号

    bool power_up; // 用于表示上电设置

    bool reset_en; // 允许软复位
    bool read_en;  // 允许读
    bool write_en; // 允许写

    uint8_t cmd[6];            // 当前收到的命令
    uint8_t cmd_stored;        // 当前收到的命令长度
    uint8_t cmd_tar_len;       // 当前命令的目录长度
    volatile int8_t cmd_state; // 当前收到的命令: 0: 命令未完成；1: 命令有效；-1: 命令无效
    void *addr;                // 存取指针

} s_sf = {
    .reg = {
        .bit.busy = 0,
        .bit.wel = 0,
    },
    .select_flag = false,
    .power_up = false,
    .read_en = false,
    .write_en = false,
    .cmd_stored = 0,
    .cmd_tar_len = 0,
    .cmd_state = 0,
    .addr = s_nor,
};

static int _cmd_route_WE(void);      // 0x06, Write 1
static int _cmd_route_WD(void);      // 0x04, Write 0
static int _cmd_route_RSR(void);     // 0x05, Read Status Register
static int _cmd_route_WSR(void);     // 0x01, Write Status Register
static int _cmd_route_RD(void);      // 0x03, Read Data
static int _cmd_route_FR(void);      // 0x0B, Fast Read
static int _cmd_route_FRDO(void);    // 0x3B, Fast Read Double Output
static int _cmd_route_PP(void);      // 0x02, Page Program
static int _cmd_route_BE(void);      // 0xD8, Block Erase (64KB)
static int _cmd_route_SE(void);      // 0x20, Sector Erase (4KB)
static int _cmd_route_CE(void);      // 0xC7, Chip Erase
static int _cmd_route_PD(void);      // 0xB9, Power Down
static int _cmd_route_RP(void);      // 0xAB, Release Power - down/Device ID
static int _cmd_route_MID(void);     // 0x90, Manufacturer/Device ID
static int _cmd_route_JEDEC(void);   // 0x9F, JEDEC ID
static int _cmd_route_SFDP(void);    // 0x5A, SFDP
static int _cmd_route_ENRESET(void); // 0x66, Enable Reset
static int _cmd_route_RESET(void);   // 0x99, Reset

static struct
{
    uint8_t cmd;        // 命令
    uint8_t len;        // 命令+参数的总长度
    int (*route)(void); // 执行处理参数。返回：1 表示允许继续解析，其他值反之
} const s_cmd_tab[] = {
    {0x06, 1, _cmd_route_WE},      // Write 1
    {0x04, 1, _cmd_route_WD},      // Write 0
    {0x05, 1, _cmd_route_RSR},     // Read Status Register
    {0x01, 2, _cmd_route_WSR},     // Write Status Register
    {0x03, 4, _cmd_route_RD},      // Read Data
    {0x0B, 0, _cmd_route_FR},      // Fast Read
    {0x3B, 0, _cmd_route_FRDO},    // Fast Read Double Output
    {0x02, 4, _cmd_route_PP},      // Page Program
    {0xD8, 4, _cmd_route_BE},      // Block Erase (64KB)
    {0x20, 4, _cmd_route_SE},      // Sector Erase (4KB)
    {0xC7, 1, _cmd_route_CE},      // Chip Erase
    {0xB9, 1, _cmd_route_PD},      // Power Down
    {0xAB, 1, _cmd_route_RP},      // Release Power - down/Device ID
    {0x90, 1, _cmd_route_MID},     // Manufacturer/Device ID
    {0x9F, 1, _cmd_route_JEDEC},   // JEDEC ID
    {0x5A, 5, _cmd_route_SFDP},    // SFDP
    {0x66, 1, _cmd_route_ENRESET}, // Enable Reset
    {0x99, 1, _cmd_route_RESET},   // Reset
};

static int _check_cmd_status(const void **src, int *len_bytes);
static bool _check_reg_read_en(void);
static bool _check_nor_read_en(void);
static bool _check_nor_write_en(void);

static int _cmd_route_WE(void) // 0x06, Write 1
{
    SYS_LOG_DBG("set write enable");
    s_sf.reg.bit.wel = 1; // 可写
    return 1;
}

static int _cmd_route_WD(void) // 0x04, Write 0
{
    SYS_LOG_DBG("set write disable");
    s_sf.reg.bit.wel = 0; // 可写
    return 1;
}

static int _cmd_route_RSR(void) // 0x05, Read Status Register
{
    SYS_LOG_DBG("Read Status Register: reg value = %#x", s_sf.reg.value);
    s_sf.addr = &s_sf.reg.value;
    s_sf.read_en = true; // 允许读
    return 1;
}

static int _cmd_route_WSR(void) // 0x01, Write Status Register
{
    SYS_LOG_DBG("Write Status Register: %#x", s_sf.cmd[1]);
    s_sf.reg.value = s_sf.cmd[1];
    return 0;
}

static int _cmd_route_RD(void) // 0x03, Read Data
{
    int addr = (int)s_sf.cmd[1] << 16 | (int)s_sf.cmd[2] << 8 | s_sf.cmd[3];
    SYS_LOG_DBG("Read data base: %#x", addr);
    s_sf.read_en = true;                // 允许读
    s_sf.addr = &((u8_t *)s_nor)[addr]; // 存取指针
    return 1;
}

static int _cmd_route_FR(void) // 0x0B, Fast Read
{
    int addr = (int)s_sf.cmd[1] << 16 | (int)s_sf.cmd[2] << 8 | s_sf.cmd[3];
    SYS_LOG_DBG("Fast Read data base: %#x", addr);
    s_sf.read_en = true;                // 允许读
    s_sf.addr = &((u8_t *)s_nor)[addr]; // 存取指针
    return 1;
}

static int _cmd_route_FRDO(void) // 0x3B, Fast Read Double Output
{
    int addr = (int)s_sf.cmd[1] << 16 | (int)s_sf.cmd[2] << 8 | s_sf.cmd[3];
    SYS_LOG_DBG("Fast Read Double Output data base: %#x", addr);
    s_sf.read_en = true;                // 允许读
    s_sf.addr = &((u8_t *)s_nor)[addr]; // 存取指针
    return 1;
}

static int _cmd_route_PP(void) // 0x02, Page Program
{
    int addr = (int)s_sf.cmd[1] << 16 | (int)s_sf.cmd[2] << 8 | s_sf.cmd[3];
    SYS_LOG_DBG("Page Program data base: %#x", addr);
    s_sf.cmd_state = 1;
    s_sf.write_en = true; // 允许写
    if (!_check_nor_write_en())
    {
        return -1;
    }

    s_sf.addr = &((u8_t *)s_nor)[addr]; // 存取指针
    return 1;
}

static int _cmd_route_BE(void) // 0xD8, Block Erase (64KB)
{
    int addr = (int)s_sf.cmd[1] << 16 | (int)s_sf.cmd[2] << 8 | s_sf.cmd[3];
    SYS_LOG_DBG("Block Erase base: %#x", addr);
    s_sf.cmd_state = 1;
    s_sf.write_en = true;
    if (!_check_nor_write_en())
    {
        return -1;
    }
    s_sf.write_en = false;

    if (addr >= sizeof(s_nor))
    {
        return -1;
    }

    int block_size = sizeof(s_nor[0]);
    int block_num = addr / block_size;
    memset(s_nor[block_num], ~0, block_size);

    s_sf.reg.bit.busy = 1; // 忙位
    return 1;
}

static int _cmd_route_SE(void) // 0x20, Sector Erase (4KB)
{
    int addr = (int)s_sf.cmd[1] << 16 | (int)s_sf.cmd[2] << 8 | s_sf.cmd[3];
    SYS_LOG_DBG("Sector Erase base: %#x", addr);
    s_sf.cmd_state = 1;
    s_sf.write_en = true;
    if (!_check_nor_write_en())
    {
        return -1;
    }
    s_sf.write_en = false;

    if (addr >= sizeof(s_nor))
    {
        return -1;
    }

    int block_size = sizeof(s_nor[0]);
    int sector_size = sizeof(s_nor[0][0]);
    int block_num = addr / block_size;
    int sector_num = (addr % block_size) / sector_size;
    memset(s_nor[block_num][sector_num], ~0, sector_size);

    s_sf.reg.bit.busy = 1; // 忙位
    return 1;
}

static int _cmd_route_CE(void) // 0xC7, Chip Erase
{
    SYS_LOG_DBG("Chip Erase");
    s_sf.cmd_state = 1;
    s_sf.write_en = true;
    if (!_check_nor_write_en())
    {
        return -1;
    }
    s_sf.write_en = false;

    memset(s_nor, ~0, sizeof(s_nor));
    s_sf.reg.bit.busy = 1; // 忙位
    return 1;
}

static int _cmd_route_PD(void) // 0xB9, Power Down
{
    SYS_LOG_DBG("Power Down");
    s_sf.power_up = false; // 用于表示上电设置
    return 1;
}

static int _cmd_route_RP(void) // 0xAB, Release Power - down/Device ID
{
    SYS_LOG_DBG("Release Power");
    s_sf.power_up = true;
    s_sf.read_en = true;
    return 1;
}

static int _cmd_route_MID(void) // 0x90, Manufacturer/Device ID
{
    SYS_LOG_DBG("mfid = %#x", s_mfid);
    s_sf.read_en = true;         // 允许读
    s_sf.addr = (void *)&s_mfid; // 存取指针
    return 1;
}

static int _cmd_route_JEDEC(void) // 0x9F, JEDEC ID
{
    SYS_LOG_DBG("read JEDEC ID");
    s_sf.read_en = true;         // 允许读
    s_sf.addr = (void *)&s_mfid; // 存取指针
    return 1;
}

static int _cmd_route_SFDP(void) // 0x5A, SFDP
{
    int addr = (int)s_sf.cmd[1] << 16 | (int)s_sf.cmd[2] << 8 | s_sf.cmd[3];
    SYS_LOG_DBG("read SFDP");
    s_sf.read_en = true;                             // 允许读
    s_sf.addr = (void *)&((uint8_t *)&s_sfdp)[addr]; // 存取指针
    return 1;
}

static int _cmd_route_ENRESET(void) // 0x66, Enable Reset
{
    SYS_LOG_DBG("Enable Reset");
    s_sf.reset_en = true; // 允许软复位
    return 0;
}

static int _cmd_route_RESET(void) // 0x99, Reset
{
    if (s_sf.reset_en)
    {
        s_sf.power_up = true;
        SYS_LOG_DBG("Reset");
    }
    else
    {
        SYS_LOG_WRN("Reset is disabled");
    }

    s_sf.reset_en = 0;
    return 0;
}

static int _check_cmd_status(const void **src, int *len_bytes)
{
    if (s_sf.select_flag == false)
    {
        SYS_LOG_WRN("CS not be selected");
        return -1;
    }

    if (s_sf.cmd_state == 0 && *len_bytes > 0)
    {
        while (*len_bytes != 0)
        {
            if (s_sf.cmd_stored >= sizeof(s_sf.cmd))
            {
                s_sf.read_en = false;
                s_sf.write_en = false;
                s_sf.cmd_state = -1;
                SYS_LOG_WRN("Unknow command: %#x", s_sf.cmd[0]);
                return s_sf.cmd_state;
            }

            s_sf.cmd[s_sf.cmd_stored++] = *((int8_t *)*src);
            *src = &((uint8_t *)*src)[1];
            *len_bytes -= 1;

            if (s_sf.cmd_stored == 1) // 第一个字节为命令
            {
                for (int i = 0; i < sizeof(s_cmd_tab) / sizeof(s_cmd_tab[0]); i++)
                {
                    if (s_cmd_tab[i].cmd == s_sf.cmd[0])
                    {
                        s_sf.cmd_tar_len = s_cmd_tab[i].len;
                        break;
                    }
                }
                if (s_sf.cmd_tar_len == 0)
                {
                    s_sf.read_en = false;
                    s_sf.write_en = false;
                    s_sf.cmd_state = -1;
                    SYS_LOG_WRN("Unknow command: %#x", s_sf.cmd[0]);
                    return s_sf.cmd_state;
                }
            }

            if (s_sf.cmd_stored == s_sf.cmd_tar_len)
            {
                for (int i = 0; i < sizeof(s_cmd_tab) / sizeof(s_cmd_tab[0]); i++)
                {
                    if (s_cmd_tab[i].cmd == s_sf.cmd[0])
                    {
                        s_sf.cmd_state = s_cmd_tab[i].route();
                        if (s_sf.cmd_state == -1)
                        {
                            SYS_LOG_WRN("s_sf.cmd_state = %d", s_sf.cmd_state);
                        }
                        return s_sf.cmd_state;
                    }
                }
            }
        }
    }

    return s_sf.cmd_state;
}

static bool _check_reg_read_en(void)
{
    _CHECK_EN(s_sf.cmd_state != 1);
    _CHECK_EN(s_sf.select_flag == false);
    _CHECK_EN(s_sf.read_en == false);

    return true;
}

static bool _check_nor_read_en(void)
{
    _CHECK_EN(s_sf.cmd_state != 1);
    _CHECK_EN(s_sf.power_up == false);
    _CHECK_EN(s_sf.select_flag == false);
    _CHECK_EN(s_sf.read_en == false);
    _CHECK_EN(s_sf.reg.bit.busy == 1);

    return true;
}

static bool _check_nor_write_en(void)
{
    _CHECK_EN(s_sf.cmd_state != 1);
    _CHECK_EN(s_sf.power_up == false);
    _CHECK_EN(s_sf.select_flag == false);
    _CHECK_EN(s_sf.write_en == false);
    _CHECK_EN(s_sf.reg.bit.busy == 1);
    _CHECK_EN(s_sf.reg.bit.wel != 1);

    return true;
}

/**
 * @brief 模拟执行设置 CS 引脚输出高电平
 *
 */
void sim_sf_set_cs(void)
{
    if (s_sf.select_flag != false)
    {
        _DO_SYS_LOG(CONFIG_SYS_LOG_DBG_ON, "\r\n");
    }
    s_sf.select_flag = false;
}

/**
 * @brief 模拟执行设置 CS 引脚输出低电平
 *
 */
void sim_sf_reset_cs(void)
{
    if (s_sf.select_flag == false)
    {
        s_sf.read_en = false;
        s_sf.write_en = false;
        s_sf.cmd_stored = 0;
        s_sf.cmd_tar_len = 0;
        s_sf.cmd_state = 0;
    }
    s_sf.select_flag = true;
}

/**
 * @brief 模拟执行写数据到 SPI 总线
 *
 * @param src 来源数据
 * @param len_bytes 数据长度（字节）
 */
void sim_sf_spi_write(const void *src, int len_bytes)
{
    if (_check_cmd_status(&src, &len_bytes) != 1)
    {
        return;
    }

    if (len_bytes == 0)
    {
        return;
    }

    if (!_check_nor_write_en())
    {
        return;
    }

    int index = (int)s_sf.addr - (int)s_nor;
    if (index < sizeof(s_nor))
    {
        int block_size = sizeof(s_nor[0]);
        int sector_size = sizeof(s_nor[0][0]);
        int page_size = 256;
        int block_num = index / block_size;
        int sector_num = (index % block_size) / sector_size;
        int page_num = (index % sector_size) / page_size;
        u8_t *sector_base = &s_nor[block_num][sector_num][page_size * page_num];
        int sector_offset = index % page_size;
        while (len_bytes)
        {
            sector_base[sector_offset] &= *(u8_t *)src;
            src = &((u8_t *)src)[1];
            len_bytes--;
            sector_offset++;
            if (sector_offset >= page_size)
            {
                sector_offset = 0;
                if (len_bytes)
                {
                    SYS_LOG_WRN("Repeating program data");
                }
            }
        }
    }
}

/**
 * @brief 模拟执行从 SPI 总线读数据
 *
 * @param dst[out] 保存数据
 * @param len_bytes 内存长度（字节）
 */
void sim_sf_spi_read(void *dst, int len_bytes)
{
    if (len_bytes == 0)
    {
        return;
    }

    if (s_sf.addr >= (void *)s_nor && s_sf.addr < (void *)&((u8_t *)s_nor)[sizeof(s_nor)]) // 读 FLASH
    {
        if (!_check_nor_read_en())
        {
            memset(dst, ~0, len_bytes);
            return;
        }

        uint remain = (uint)s_nor + sizeof(s_nor) - (uint)s_sf.addr;
        uint size = len_bytes;
        if (size > remain)
            size = remain;

        memcpy(dst, s_sf.addr, size);
        s_sf.addr = &((u8_t *)s_sf.addr)[len_bytes];

        if (len_bytes > size)
        {
            len_bytes -= size;
            dst = &((u8_t *)dst)[size];
            memset(dst, ~0, len_bytes);
        }
    }
    else // 读寄存器
    {
        if (!_check_reg_read_en())
        {
            memset(dst, ~0, len_bytes);
            return;
        }

        if (s_sf.addr == &s_sf.reg.value) // 读寄状态存器
        {
            *(uint8_t *)dst = s_sf.reg.value;
            if (s_sf.reg.bit.busy)
            {
                SYS_LOG_DBG("s_sf.reg.bit.busy = %d", s_sf.reg.bit.busy);
            }
            s_sf.reg.bit.busy = 0;
        }
        else if (s_sf.addr == &s_mfid)
        {
            uint size = sizeof(s_nor);
            for (int i = 0; i < sizeof(size) * 8; i++)
            {
                if (size >> (i + 1) == 0)
                {
                    u8_t mfid[] = {
                        s_mfid,
                        i - 1,
                        0x40,
                    };
                    memcpy(dst, mfid, len_bytes);
                    return;
                }
            }
        }
        else
        {
            memcpy(dst, s_sf.addr, len_bytes);
        }
    }
}
